package com.fang.system.io;

import org.junit.Test;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @author fanglingxiao
 * @version 1.0
 * @description TODO
 * @date 2022/7/12 3:10 下午
 **/
public class OSFileIO {
    private static final byte[] data = "123456789\n".getBytes();
    private static final String path = "/java/study/testfileio/out.txt";

    public static void main(String[] args) throws Exception {
        switch (args[0]) {
            case "0":
                testBasicFileIO();
                break;
            case "1":
                testBufferedFileIO();
                break;
            case "2":
                testRandomAccessFileWrite();
            case "3":
//                whatByteBuffer();
            default:
        }
    }

    /**
     * 最基本的file写
     */
    public static void testBasicFileIO() throws Exception {
        File file = new File(path);
        FileOutputStream out = new FileOutputStream(file);
        while (true) {
//            Thread.sleep(10);
            out.write(data);
        }
    }

    /**
     * 测试buffer文件IO
     * jvm  8kB的缓存buffer 满了之后调用 syscall  write(8KBbyte[])
     * 所以对比基本的file写，buffer文件IO更少的调用系统write，以及更少的系统切换
     */
    public static void testBufferedFileIO() throws Exception {
        File file = new File(path);
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
        while (true) {
            Thread.sleep(10);
            out.write(data);
        }
    }

    /**
     * 测试文件NIO
     */
    public static void testRandomAccessFileWrite() throws Exception {
        /**
         * RandomAccessFile提供了读写流的操作（相比out或者input都是单一的读写）
         * 如果RandomAccessFile作为输出流时，写出到的文件如果不存在，则在执行过程中自动创建
         * 如果写出到的文件存在，则会对原有文件内容进行覆盖
         */
        RandomAccessFile raf = new RandomAccessFile(path, "rw");
        /**
         * RandomAccessFile的特点在于任意访问文件的任意位置，可以说是基于字节访问的，可通过getFilePointer()获取当前指针所在位置，
         * 可通过seek()移动指针，这体现了它的任意性，也是其与其他I/O流相比，自成一派的原因
         *
         * 一句话总结：seek用于设置文件指针位置，设置后ras会从当前指针的下一位读取到或写入到
         *
         */
        raf.write("hello fanglingxiao\n".getBytes());
        raf.write("hello io\n".getBytes());
        System.out.println("write------------");
        System.in.read();
        // 讲raf的指针移动到4，则读写raf是从第5个字节开始
        raf.seek(4);
        raf.write("ooxx".getBytes());

        System.out.println("seek---------");
        System.in.read();

        FileChannel rafchannel = raf.getChannel();
        //mmap  堆外  和文件映射的   byte  not  objtect
        // lsof -p pid查看进程信息以及内存分配
        MappedByteBuffer map = rafchannel.map(FileChannel.MapMode.READ_WRITE, 0, 4096);

        map.put("@@@".getBytes());
        //不是系统调用  数据通过堆外内存mmap映射会到达 内核的pagecache
        //曾经是需要out.write()  这样的系统调用（int 0x80指令中断 内核态用户态切换）才能让程序的data 进入内核的pagecache
        //曾经必须有用户态内核态切换
        //mmap的内存映射，依然是内核的pagecache体系所约束的！！！
        //换言之，可能会出现丢数据的问题
        //可以去github上找一些 其他C程序员写的jni扩展库，使用linux内核的Direct IO
        //直接IO是忽略linux的pagecache
        //是把pagecache  交给了程序自己开辟一个字节数组当作pagecache，动用代码逻辑来维护一致性/dirty。。。一系列复杂问题

        System.out.println("map--put--------");
        System.in.read();

//        map.force(); //  flush

        raf.seek(0);

        ByteBuffer buffer = ByteBuffer.allocate(8192);
//        ByteBuffer buffer = ByteBuffer.allocateDirect(1024);

        int read = rafchannel.read(buffer);   //buffer.put()
        System.out.println(buffer);
        buffer.flip();
        System.out.println(buffer);

        for (int i = 0; i < buffer.limit(); i++) {
            Thread.sleep(200);
            System.out.print(((char) buffer.get(i)));
        }


    }


    @Test
    public void whatByteBuffer() {

//        ByteBuffer buffer = ByteBuffer.allocate(1024);
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024);


        System.out.println("postition: " + buffer.position());
        System.out.println("limit: " + buffer.limit());
        System.out.println("capacity: " + buffer.capacity());
        System.out.println("mark: " + buffer);

        buffer.put("123".getBytes());

        System.out.println("-------------put:123......");
        System.out.println("mark: " + buffer);

        buffer.flip();   //读写交替

        System.out.println("-------------flip......");
        System.out.println("mark: " + buffer);

        buffer.get();

        System.out.println("-------------get......");
        System.out.println("mark: " + buffer);

        buffer.compact();

        System.out.println("-------------compact......");
        System.out.println("mark: " + buffer);

        buffer.clear();

        System.out.println("-------------clear......");
        System.out.println("mark: " + buffer);
    }
}
